home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / Pakiet bezpieczenstwa / mini Pentoo LiveCD 2006.1 / mpentoo-2006.1.iso / livecd.squashfs / usr / lib / python2.4 / mhlib.pyo (.txt) < prev    next >
Python Compiled Bytecode  |  2005-10-18  |  33KB  |  1,269 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (Python 2.4)
  3.  
  4. """MH interface -- purely object-oriented (well, almost)
  5.  
  6. Executive summary:
  7.  
  8. import mhlib
  9.  
  10. mh = mhlib.MH()         # use default mailbox directory and profile
  11. mh = mhlib.MH(mailbox)  # override mailbox location (default from profile)
  12. mh = mhlib.MH(mailbox, profile) # override mailbox and profile
  13.  
  14. mh.error(format, ...)   # print error message -- can be overridden
  15. s = mh.getprofile(key)  # profile entry (None if not set)
  16. path = mh.getpath()     # mailbox pathname
  17. name = mh.getcontext()  # name of current folder
  18. mh.setcontext(name)     # set name of current folder
  19.  
  20. list = mh.listfolders() # names of top-level folders
  21. list = mh.listallfolders() # names of all folders, including subfolders
  22. list = mh.listsubfolders(name) # direct subfolders of given folder
  23. list = mh.listallsubfolders(name) # all subfolders of given folder
  24.  
  25. mh.makefolder(name)     # create new folder
  26. mh.deletefolder(name)   # delete folder -- must have no subfolders
  27.  
  28. f = mh.openfolder(name) # new open folder object
  29.  
  30. f.error(format, ...)    # same as mh.error(format, ...)
  31. path = f.getfullname()  # folder's full pathname
  32. path = f.getsequencesfilename() # full pathname of folder's sequences file
  33. path = f.getmessagefilename(n)  # full pathname of message n in folder
  34.  
  35. list = f.listmessages() # list of messages in folder (as numbers)
  36. n = f.getcurrent()      # get current message
  37. f.setcurrent(n)         # set current message
  38. list = f.parsesequence(seq)     # parse msgs syntax into list of messages
  39. n = f.getlast()         # get last message (0 if no messagse)
  40. f.setlast(n)            # set last message (internal use only)
  41.  
  42. dict = f.getsequences() # dictionary of sequences in folder {name: list}
  43. f.putsequences(dict)    # write sequences back to folder
  44.  
  45. f.createmessage(n, fp)  # add message from file f as number n
  46. f.removemessages(list)  # remove messages in list from folder
  47. f.refilemessages(list, tofolder) # move messages in list to other folder
  48. f.movemessage(n, tofolder, ton)  # move one message to a given destination
  49. f.copymessage(n, tofolder, ton)  # copy one message to a given destination
  50.  
  51. m = f.openmessage(n)    # new open message object (costs a file descriptor)
  52. m is a derived class of mimetools.Message(rfc822.Message), with:
  53. s = m.getheadertext()   # text of message's headers
  54. s = m.getheadertext(pred) # text of message's headers, filtered by pred
  55. s = m.getbodytext()     # text of message's body, decoded
  56. s = m.getbodytext(0)    # text of message's body, not decoded
  57. """
  58. MH_PROFILE = '~/.mh_profile'
  59. PATH = '~/Mail'
  60. MH_SEQUENCES = '.mh_sequences'
  61. FOLDER_PROTECT = 448
  62. import os
  63. import sys
  64. import re
  65. import mimetools
  66. import multifile
  67. import shutil
  68. from bisect import bisect
  69. __all__ = [
  70.     'MH',
  71.     'Error',
  72.     'Folder',
  73.     'Message']
  74.  
  75. class Error(Exception):
  76.     pass
  77.  
  78.  
  79. class MH:
  80.     '''Class representing a particular collection of folders.
  81.     Optional constructor arguments are the pathname for the directory
  82.     containing the collection, and the MH profile to use.
  83.     If either is omitted or empty a default is used; the default
  84.     directory is taken from the MH profile if it is specified there.'''
  85.     
  86.     def __init__(self, path = None, profile = None):
  87.         '''Constructor.'''
  88.         if profile is None:
  89.             profile = MH_PROFILE
  90.         
  91.         self.profile = os.path.expanduser(profile)
  92.         if path is None:
  93.             path = self.getprofile('Path')
  94.         
  95.         if not path:
  96.             path = PATH
  97.         
  98.         if not os.path.isabs(path) and path[0] != '~':
  99.             path = os.path.join('~', path)
  100.         
  101.         path = os.path.expanduser(path)
  102.         if not os.path.isdir(path):
  103.             raise Error, 'MH() path not found'
  104.         
  105.         self.path = path
  106.  
  107.     
  108.     def __repr__(self):
  109.         '''String representation.'''
  110.         return 'MH(%r, %r)' % (self.path, self.profile)
  111.  
  112.     
  113.     def error(self, msg, *args):
  114.         '''Routine to print an error.  May be overridden by a derived class.'''
  115.         sys.stderr.write('MH error: %s\n' % msg % args)
  116.  
  117.     
  118.     def getprofile(self, key):
  119.         '''Return a profile entry, None if not found.'''
  120.         return pickline(self.profile, key)
  121.  
  122.     
  123.     def getpath(self):
  124.         """Return the path (the name of the collection's directory)."""
  125.         return self.path
  126.  
  127.     
  128.     def getcontext(self):
  129.         '''Return the name of the current folder.'''
  130.         context = pickline(os.path.join(self.getpath(), 'context'), 'Current-Folder')
  131.         if not context:
  132.             context = 'inbox'
  133.         
  134.         return context
  135.  
  136.     
  137.     def setcontext(self, context):
  138.         '''Set the name of the current folder.'''
  139.         fn = os.path.join(self.getpath(), 'context')
  140.         f = open(fn, 'w')
  141.         f.write('Current-Folder: %s\n' % context)
  142.         f.close()
  143.  
  144.     
  145.     def listfolders(self):
  146.         '''Return the names of the top-level folders.'''
  147.         folders = []
  148.         path = self.getpath()
  149.         for name in os.listdir(path):
  150.             fullname = os.path.join(path, name)
  151.             if os.path.isdir(fullname):
  152.                 folders.append(name)
  153.                 continue
  154.         
  155.         folders.sort()
  156.         return folders
  157.  
  158.     
  159.     def listsubfolders(self, name):
  160.         '''Return the names of the subfolders in a given folder
  161.         (prefixed with the given folder name).'''
  162.         fullname = os.path.join(self.path, name)
  163.         nlinks = os.stat(fullname).st_nlink
  164.         if nlinks <= 2:
  165.             return []
  166.         
  167.         subfolders = []
  168.         subnames = os.listdir(fullname)
  169.         for subname in subnames:
  170.             fullsubname = os.path.join(fullname, subname)
  171.             if os.path.isdir(fullsubname):
  172.                 name_subname = os.path.join(name, subname)
  173.                 subfolders.append(name_subname)
  174.                 nlinks = nlinks - 1
  175.                 if nlinks <= 2:
  176.                     break
  177.                 
  178.             nlinks <= 2
  179.         
  180.         subfolders.sort()
  181.         return subfolders
  182.  
  183.     
  184.     def listallfolders(self):
  185.         '''Return the names of all folders and subfolders, recursively.'''
  186.         return self.listallsubfolders('')
  187.  
  188.     
  189.     def listallsubfolders(self, name):
  190.         '''Return the names of subfolders in a given folder, recursively.'''
  191.         fullname = os.path.join(self.path, name)
  192.         nlinks = os.stat(fullname).st_nlink
  193.         if nlinks <= 2:
  194.             return []
  195.         
  196.         subfolders = []
  197.         subnames = os.listdir(fullname)
  198.         for subname in subnames:
  199.             if subname[0] == ',' or isnumeric(subname):
  200.                 continue
  201.             
  202.             fullsubname = os.path.join(fullname, subname)
  203.             if os.path.isdir(fullsubname):
  204.                 name_subname = os.path.join(name, subname)
  205.                 subfolders.append(name_subname)
  206.                 if not os.path.islink(fullsubname):
  207.                     subsubfolders = self.listallsubfolders(name_subname)
  208.                     subfolders = subfolders + subsubfolders
  209.                 
  210.                 nlinks = nlinks - 1
  211.                 if nlinks <= 2:
  212.                     break
  213.                 
  214.             nlinks <= 2
  215.         
  216.         subfolders.sort()
  217.         return subfolders
  218.  
  219.     
  220.     def openfolder(self, name):
  221.         '''Return a new Folder object for the named folder.'''
  222.         return Folder(self, name)
  223.  
  224.     
  225.     def makefolder(self, name):
  226.         '''Create a new folder (or raise os.error if it cannot be created).'''
  227.         protect = pickline(self.profile, 'Folder-Protect')
  228.         if protect and isnumeric(protect):
  229.             mode = int(protect, 8)
  230.         else:
  231.             mode = FOLDER_PROTECT
  232.         os.mkdir(os.path.join(self.getpath(), name), mode)
  233.  
  234.     
  235.     def deletefolder(self, name):
  236.         '''Delete a folder.  This removes files in the folder but not
  237.         subdirectories.  Raise os.error if deleting the folder itself fails.'''
  238.         fullname = os.path.join(self.getpath(), name)
  239.         for subname in os.listdir(fullname):
  240.             fullsubname = os.path.join(fullname, subname)
  241.             
  242.             try:
  243.                 os.unlink(fullsubname)
  244.             continue
  245.             except os.error:
  246.                 self.error('%s not deleted, continuing...' % fullsubname)
  247.                 continue
  248.             
  249.  
  250.         
  251.         os.rmdir(fullname)
  252.  
  253.  
  254. numericprog = re.compile('^[1-9][0-9]*$')
  255.  
  256. def isnumeric(str):
  257.     return numericprog.match(str) is not None
  258.  
  259.  
  260. class Folder:
  261.     '''Class representing a particular folder.'''
  262.     
  263.     def __init__(self, mh, name):
  264.         '''Constructor.'''
  265.         self.mh = mh
  266.         self.name = name
  267.         if not os.path.isdir(self.getfullname()):
  268.             raise Error, 'no folder %s' % name
  269.         
  270.  
  271.     
  272.     def __repr__(self):
  273.         '''String representation.'''
  274.         return 'Folder(%r, %r)' % (self.mh, self.name)
  275.  
  276.     
  277.     def error(self, *args):
  278.         '''Error message handler.'''
  279.         self.mh.error(*args)
  280.  
  281.     
  282.     def getfullname(self):
  283.         '''Return the full pathname of the folder.'''
  284.         return os.path.join(self.mh.path, self.name)
  285.  
  286.     
  287.     def getsequencesfilename(self):
  288.         """Return the full pathname of the folder's sequences file."""
  289.         return os.path.join(self.getfullname(), MH_SEQUENCES)
  290.  
  291.     
  292.     def getmessagefilename(self, n):
  293.         '''Return the full pathname of a message in the folder.'''
  294.         return os.path.join(self.getfullname(), str(n))
  295.  
  296.     
  297.     def listsubfolders(self):
  298.         '''Return list of direct subfolders.'''
  299.         return self.mh.listsubfolders(self.name)
  300.  
  301.     
  302.     def listallsubfolders(self):
  303.         '''Return list of all subfolders.'''
  304.         return self.mh.listallsubfolders(self.name)
  305.  
  306.     
  307.     def listmessages(self):
  308.         '''Return the list of messages currently present in the folder.
  309.         As a side effect, set self.last to the last message (or 0).'''
  310.         messages = []
  311.         match = numericprog.match
  312.         append = messages.append
  313.         for name in os.listdir(self.getfullname()):
  314.             if match(name):
  315.                 append(name)
  316.                 continue
  317.         
  318.         messages = map(int, messages)
  319.         messages.sort()
  320.         if messages:
  321.             self.last = messages[-1]
  322.         else:
  323.             self.last = 0
  324.         return messages
  325.  
  326.     
  327.     def getsequences(self):
  328.         '''Return the set of sequences for the folder.'''
  329.         sequences = { }
  330.         fullname = self.getsequencesfilename()
  331.         
  332.         try:
  333.             f = open(fullname, 'r')
  334.         except IOError:
  335.             return sequences
  336.  
  337.         while None:
  338.             line = f.readline()
  339.             if not line:
  340.                 break
  341.             
  342.             fields = line.split(':')
  343.             if len(fields) != 2:
  344.                 self.error('bad sequence in %s: %s' % (fullname, line.strip()))
  345.             
  346.             key = fields[0].strip()
  347.             value = IntSet(fields[1].strip(), ' ').tolist()
  348.             sequences[key] = value
  349.         return sequences
  350.  
  351.     
  352.     def putsequences(self, sequences):
  353.         '''Write the set of sequences back to the folder.'''
  354.         fullname = self.getsequencesfilename()
  355.         f = None
  356.         for key, seq in sequences.iteritems():
  357.             s = IntSet('', ' ')
  358.             s.fromlist(seq)
  359.             if not f:
  360.                 f = open(fullname, 'w')
  361.             
  362.             f.write('%s: %s\n' % (key, s.tostring()))
  363.         
  364.         if not f:
  365.             
  366.             try:
  367.                 os.unlink(fullname)
  368.             except os.error:
  369.                 pass
  370.             except:
  371.                 None<EXCEPTION MATCH>os.error
  372.             
  373.  
  374.         None<EXCEPTION MATCH>os.error
  375.         f.close()
  376.  
  377.     
  378.     def getcurrent(self):
  379.         '''Return the current message.  Raise Error when there is none.'''
  380.         seqs = self.getsequences()
  381.         
  382.         try:
  383.             return max(seqs['cur'])
  384.         except (ValueError, KeyError):
  385.             raise Error, 'no cur message'
  386.  
  387.  
  388.     
  389.     def setcurrent(self, n):
  390.         '''Set the current message.'''
  391.         updateline(self.getsequencesfilename(), 'cur', str(n), 0)
  392.  
  393.     
  394.     def parsesequence(self, seq):
  395.         '''Parse an MH sequence specification into a message list.
  396.         Attempt to mimic mh-sequence(5) as close as possible.
  397.         Also attempt to mimic observed behavior regarding which
  398.         conditions cause which error messages.'''
  399.         all = self.listmessages()
  400.         if not all:
  401.             raise Error, 'no messages in %s' % self.name
  402.         
  403.         if seq == 'all':
  404.             return all
  405.         
  406.         i = seq.find(':')
  407.         if i >= 0:
  408.             head = seq[:i]
  409.             dir = ''
  410.             tail = seq[i + 1:]
  411.             if tail[:1] in '-+':
  412.                 dir = tail[:1]
  413.                 tail = tail[1:]
  414.             
  415.             if not isnumeric(tail):
  416.                 raise Error, 'bad message list %s' % seq
  417.             
  418.             
  419.             try:
  420.                 count = int(tail)
  421.             except (ValueError, OverflowError):
  422.                 count = len(all)
  423.  
  424.             
  425.             try:
  426.                 anchor = self._parseindex(head, all)
  427.             except Error:
  428.                 msg = None
  429.                 seqs = self.getsequences()
  430.                 if head not in seqs:
  431.                     if not msg:
  432.                         msg = 'bad message list %s' % seq
  433.                     
  434.                     raise Error, msg, sys.exc_info()[2]
  435.                 
  436.                 msgs = seqs[head]
  437.                 if not msgs:
  438.                     raise Error, 'sequence %s empty' % head
  439.                 
  440.                 if dir == '-':
  441.                     return msgs[-count:]
  442.                 else:
  443.                     return msgs[:count]
  444.             except:
  445.                 dir == '-'
  446.  
  447.             if not dir:
  448.                 if head in ('prev', 'last'):
  449.                     dir = '-'
  450.                 
  451.             
  452.             if dir == '-':
  453.                 i = bisect(all, anchor)
  454.                 return all[max(0, i - count):i]
  455.             else:
  456.                 i = bisect(all, anchor - 1)
  457.                 return all[i:i + count]
  458.         
  459.         i = seq.find('-')
  460.         if i >= 0:
  461.             begin = self._parseindex(seq[:i], all)
  462.             end = self._parseindex(seq[i + 1:], all)
  463.             i = bisect(all, begin - 1)
  464.             j = bisect(all, end)
  465.             r = all[i:j]
  466.             if not r:
  467.                 raise Error, 'bad message list %s' % seq
  468.             
  469.             return r
  470.         
  471.         
  472.         try:
  473.             n = self._parseindex(seq, all)
  474.         except Error:
  475.             msg = None
  476.             seqs = self.getsequences()
  477.             if seq not in seqs:
  478.                 if not msg:
  479.                     msg = 'bad message list %s' % seq
  480.                 
  481.                 raise Error, msg
  482.             
  483.             return seqs[seq]
  484.  
  485.         if n not in all:
  486.             if isnumeric(seq):
  487.                 raise Error, "message %d doesn't exist" % n
  488.             else:
  489.                 raise Error, 'no %s message' % seq
  490.         else:
  491.             return [
  492.                 n]
  493.  
  494.     
  495.     def _parseindex(self, seq, all):
  496.         '''Internal: parse a message number (or cur, first, etc.).'''
  497.         if isnumeric(seq):
  498.             
  499.             try:
  500.                 return int(seq)
  501.             except (OverflowError, ValueError):
  502.                 return sys.maxint
  503.             except:
  504.                 None<EXCEPTION MATCH>(OverflowError, ValueError)
  505.             
  506.  
  507.         None<EXCEPTION MATCH>(OverflowError, ValueError)
  508.         if seq in ('cur', '.'):
  509.             return self.getcurrent()
  510.         
  511.         if seq == 'first':
  512.             return all[0]
  513.         
  514.         if seq == 'last':
  515.             return all[-1]
  516.         
  517.         if seq == 'next':
  518.             n = self.getcurrent()
  519.             i = bisect(all, n)
  520.             
  521.             try:
  522.                 return all[i]
  523.             except IndexError:
  524.                 raise Error, 'no next message'
  525.             except:
  526.                 None<EXCEPTION MATCH>IndexError
  527.             
  528.  
  529.         None<EXCEPTION MATCH>IndexError
  530.         if seq == 'prev':
  531.             n = self.getcurrent()
  532.             i = bisect(all, n - 1)
  533.             if i == 0:
  534.                 raise Error, 'no prev message'
  535.             
  536.             
  537.             try:
  538.                 return all[i - 1]
  539.             except IndexError:
  540.                 raise Error, 'no prev message'
  541.             except:
  542.                 None<EXCEPTION MATCH>IndexError
  543.             
  544.  
  545.         None<EXCEPTION MATCH>IndexError
  546.         raise Error, None
  547.  
  548.     
  549.     def openmessage(self, n):
  550.         '''Open a message -- returns a Message object.'''
  551.         return Message(self, n)
  552.  
  553.     
  554.     def removemessages(self, list):
  555.         '''Remove one or more messages -- may raise os.error.'''
  556.         errors = []
  557.         deleted = []
  558.         for n in list:
  559.             path = self.getmessagefilename(n)
  560.             commapath = self.getmessagefilename(',' + str(n))
  561.             
  562.             try:
  563.                 os.unlink(commapath)
  564.             except os.error:
  565.                 pass
  566.  
  567.             
  568.             try:
  569.                 os.rename(path, commapath)
  570.             except os.error:
  571.                 msg = None
  572.                 errors.append(msg)
  573.                 continue
  574.  
  575.             deleted.append(n)
  576.         
  577.         if deleted:
  578.             self.removefromallsequences(deleted)
  579.         
  580.         if errors:
  581.             if len(errors) == 1:
  582.                 raise os.error, errors[0]
  583.             else:
  584.                 raise os.error, ('multiple errors:', errors)
  585.         
  586.  
  587.     
  588.     def refilemessages(self, list, tofolder, keepsequences = 0):
  589.         """Refile one or more messages -- may raise os.error.
  590.         'tofolder' is an open folder object."""
  591.         errors = []
  592.         refiled = { }
  593.         for n in list:
  594.             ton = tofolder.getlast() + 1
  595.             path = self.getmessagefilename(n)
  596.             topath = tofolder.getmessagefilename(ton)
  597.             
  598.             try:
  599.                 os.rename(path, topath)
  600.             except os.error:
  601.                 
  602.                 try:
  603.                     shutil.copy2(path, topath)
  604.                     os.unlink(path)
  605.                 except (IOError, os.error):
  606.                     msg = None
  607.                     errors.append(msg)
  608.                     
  609.                     try:
  610.                         os.unlink(topath)
  611.                     continue
  612.                     except os.error:
  613.                         continue
  614.                     
  615.  
  616.                 except:
  617.                     None<EXCEPTION MATCH>(IOError, os.error)
  618.                 
  619.  
  620.                 None<EXCEPTION MATCH>(IOError, os.error)
  621.  
  622.             tofolder.setlast(ton)
  623.             refiled[n] = ton
  624.         
  625.         if refiled:
  626.             if keepsequences:
  627.                 tofolder._copysequences(self, refiled.items())
  628.             
  629.             self.removefromallsequences(refiled.keys())
  630.         
  631.         if errors:
  632.             if len(errors) == 1:
  633.                 raise os.error, errors[0]
  634.             else:
  635.                 raise os.error, ('multiple errors:', errors)
  636.         
  637.  
  638.     
  639.     def _copysequences(self, fromfolder, refileditems):
  640.         '''Helper for refilemessages() to copy sequences.'''
  641.         fromsequences = fromfolder.getsequences()
  642.         tosequences = self.getsequences()
  643.         changed = 0
  644.         for name, seq in fromsequences.items():
  645.             
  646.             try:
  647.                 toseq = tosequences[name]
  648.                 new = 0
  649.             except KeyError:
  650.                 toseq = []
  651.                 new = 1
  652.  
  653.             for fromn, ton in refileditems:
  654.                 if fromn in seq:
  655.                     toseq.append(ton)
  656.                     changed = 1
  657.                     continue
  658.             
  659.             if new and toseq:
  660.                 tosequences[name] = toseq
  661.                 continue
  662.         
  663.         if changed:
  664.             self.putsequences(tosequences)
  665.         
  666.  
  667.     
  668.     def movemessage(self, n, tofolder, ton):
  669.         '''Move one message over a specific destination message,
  670.         which may or may not already exist.'''
  671.         path = self.getmessagefilename(n)
  672.         f = open(path)
  673.         f.close()
  674.         del f
  675.         topath = tofolder.getmessagefilename(ton)
  676.         backuptopath = tofolder.getmessagefilename(',%d' % ton)
  677.         
  678.         try:
  679.             os.rename(topath, backuptopath)
  680.         except os.error:
  681.             pass
  682.  
  683.         
  684.         try:
  685.             os.rename(path, topath)
  686.         except os.error:
  687.             ok = 0
  688.             
  689.             try:
  690.                 tofolder.setlast(None)
  691.                 shutil.copy2(path, topath)
  692.                 ok = 1
  693.             finally:
  694.                 if not ok:
  695.                     
  696.                     try:
  697.                         os.unlink(topath)
  698.                     except os.error:
  699.                         pass
  700.                     except:
  701.                         None<EXCEPTION MATCH>os.error
  702.                     
  703.  
  704.  
  705.             os.unlink(path)
  706.  
  707.         self.removefromallsequences([
  708.             n])
  709.  
  710.     
  711.     def copymessage(self, n, tofolder, ton):
  712.         '''Copy one message over a specific destination message,
  713.         which may or may not already exist.'''
  714.         path = self.getmessagefilename(n)
  715.         f = open(path)
  716.         f.close()
  717.         del f
  718.         topath = tofolder.getmessagefilename(ton)
  719.         backuptopath = tofolder.getmessagefilename(',%d' % ton)
  720.         
  721.         try:
  722.             os.rename(topath, backuptopath)
  723.         except os.error:
  724.             pass
  725.  
  726.         ok = 0
  727.         
  728.         try:
  729.             tofolder.setlast(None)
  730.             shutil.copy2(path, topath)
  731.             ok = 1
  732.         finally:
  733.             if not ok:
  734.                 
  735.                 try:
  736.                     os.unlink(topath)
  737.                 except os.error:
  738.                     pass
  739.                 except:
  740.                     None<EXCEPTION MATCH>os.error
  741.                 
  742.  
  743.  
  744.  
  745.     
  746.     def createmessage(self, n, txt):
  747.         '''Create a message, with text from the open file txt.'''
  748.         path = self.getmessagefilename(n)
  749.         backuppath = self.getmessagefilename(',%d' % n)
  750.         
  751.         try:
  752.             os.rename(path, backuppath)
  753.         except os.error:
  754.             pass
  755.  
  756.         ok = 0
  757.         BUFSIZE = 16 * 1024
  758.         
  759.         try:
  760.             f = open(path, 'w')
  761.             while None:
  762.                 buf = txt.read(BUFSIZE)
  763.                 if not buf:
  764.                     break
  765.                 
  766.             f.close()
  767.             ok = 1
  768.         finally:
  769.             if not ok:
  770.                 
  771.                 try:
  772.                     os.unlink(path)
  773.                 except os.error:
  774.                     pass
  775.                 except:
  776.                     None<EXCEPTION MATCH>os.error
  777.                 
  778.  
  779.  
  780.  
  781.     
  782.     def removefromallsequences(self, list):
  783.         """Remove one or more messages from all sequences (including last)
  784.         -- but not from 'cur'!!!"""
  785.         if hasattr(self, 'last') and self.last in list:
  786.             del self.last
  787.         
  788.         sequences = self.getsequences()
  789.         changed = 0
  790.         for name, seq in sequences.items():
  791.             if name == 'cur':
  792.                 continue
  793.             
  794.             for n in list:
  795.                 if n in seq:
  796.                     seq.remove(n)
  797.                     changed = 1
  798.                     if not seq:
  799.                         del sequences[name]
  800.                     
  801.                 seq
  802.             
  803.         
  804.         if changed:
  805.             self.putsequences(sequences)
  806.         
  807.  
  808.     
  809.     def getlast(self):
  810.         '''Return the last message number.'''
  811.         if not hasattr(self, 'last'):
  812.             self.listmessages()
  813.         
  814.         return self.last
  815.  
  816.     
  817.     def setlast(self, last):
  818.         '''Set the last message number.'''
  819.         if last is None:
  820.             if hasattr(self, 'last'):
  821.                 del self.last
  822.             
  823.         else:
  824.             self.last = last
  825.  
  826.  
  827.  
  828. class Message(mimetools.Message):
  829.     
  830.     def __init__(self, f, n, fp = None):
  831.         '''Constructor.'''
  832.         self.folder = f
  833.         self.number = n
  834.         if fp is None:
  835.             path = f.getmessagefilename(n)
  836.             fp = open(path, 'r')
  837.         
  838.         mimetools.Message.__init__(self, fp)
  839.  
  840.     
  841.     def __repr__(self):
  842.         '''String representation.'''
  843.         return 'Message(%s, %s)' % (repr(self.folder), self.number)
  844.  
  845.     
  846.     def getheadertext(self, pred = None):
  847.         """Return the message's header text as a string.  If an
  848.         argument is specified, it is used as a filter predicate to
  849.         decide which headers to return (its argument is the header
  850.         name converted to lower case)."""
  851.         if pred is None:
  852.             return ''.join(self.headers)
  853.         
  854.         headers = []
  855.         hit = 0
  856.         for line in self.headers:
  857.             if not line[0].isspace():
  858.                 i = line.find(':')
  859.                 if i > 0:
  860.                     hit = pred(line[:i].lower())
  861.                 
  862.             
  863.             if hit:
  864.                 headers.append(line)
  865.                 continue
  866.         
  867.         return ''.join(headers)
  868.  
  869.     
  870.     def getbodytext(self, decode = 1):
  871.         """Return the message's body text as string.  This undoes a
  872.         Content-Transfer-Encoding, but does not interpret other MIME
  873.         features (e.g. multipart messages).  To suppress decoding,
  874.         pass 0 as an argument."""
  875.         self.fp.seek(self.startofbody)
  876.         encoding = self.getencoding()
  877.         if not decode or encoding in ('', '7bit', '8bit', 'binary'):
  878.             return self.fp.read()
  879.         
  880.         StringIO = StringIO
  881.         import StringIO
  882.         output = StringIO()
  883.         mimetools.decode(self.fp, output, encoding)
  884.         return output.getvalue()
  885.  
  886.     
  887.     def getbodyparts(self):
  888.         """Only for multipart messages: return the message's body as a
  889.         list of SubMessage objects.  Each submessage object behaves
  890.         (almost) as a Message object."""
  891.         if self.getmaintype() != 'multipart':
  892.             raise Error, 'Content-Type is not multipart/*'
  893.         
  894.         bdry = self.getparam('boundary')
  895.         if not bdry:
  896.             raise Error, 'multipart/* without boundary param'
  897.         
  898.         self.fp.seek(self.startofbody)
  899.         mf = multifile.MultiFile(self.fp)
  900.         mf.push(bdry)
  901.         parts = []
  902.         while mf.next():
  903.             n = '%s.%r' % (self.number, 1 + len(parts))
  904.             part = SubMessage(self.folder, n, mf)
  905.             parts.append(part)
  906.         mf.pop()
  907.         return parts
  908.  
  909.     
  910.     def getbody(self):
  911.         '''Return body, either a string or a list of messages.'''
  912.         if self.getmaintype() == 'multipart':
  913.             return self.getbodyparts()
  914.         else:
  915.             return self.getbodytext()
  916.  
  917.  
  918.  
  919. class SubMessage(Message):
  920.     
  921.     def __init__(self, f, n, fp):
  922.         '''Constructor.'''
  923.         Message.__init__(self, f, n, fp)
  924.         if self.getmaintype() == 'multipart':
  925.             self.body = Message.getbodyparts(self)
  926.         else:
  927.             self.body = Message.getbodytext(self)
  928.         self.bodyencoded = Message.getbodytext(self, decode = 0)
  929.  
  930.     
  931.     def __repr__(self):
  932.         '''String representation.'''
  933.         f = self.folder
  934.         n = self.number
  935.         fp = self.fp
  936.         return 'SubMessage(%s, %s, %s)' % (f, n, fp)
  937.  
  938.     
  939.     def getbodytext(self, decode = 1):
  940.         if not decode:
  941.             return self.bodyencoded
  942.         
  943.         if type(self.body) == type(''):
  944.             return self.body
  945.         
  946.  
  947.     
  948.     def getbodyparts(self):
  949.         if type(self.body) == type([]):
  950.             return self.body
  951.         
  952.  
  953.     
  954.     def getbody(self):
  955.         return self.body
  956.  
  957.  
  958.  
  959. class IntSet:
  960.     """Class implementing sets of integers.
  961.  
  962.     This is an efficient representation for sets consisting of several
  963.     continuous ranges, e.g. 1-100,200-400,402-1000 is represented
  964.     internally as a list of three pairs: [(1,100), (200,400),
  965.     (402,1000)].  The internal representation is always kept normalized.
  966.  
  967.     The constructor has up to three arguments:
  968.     - the string used to initialize the set (default ''),
  969.     - the separator between ranges (default ',')
  970.     - the separator between begin and end of a range (default '-')
  971.     The separators must be strings (not regexprs) and should be different.
  972.  
  973.     The tostring() function yields a string that can be passed to another
  974.     IntSet constructor; __repr__() is a valid IntSet constructor itself.
  975.     """
  976.     
  977.     def __init__(self, data = None, sep = ',', rng = '-'):
  978.         self.pairs = []
  979.         self.sep = sep
  980.         self.rng = rng
  981.         if data:
  982.             self.fromstring(data)
  983.         
  984.  
  985.     
  986.     def reset(self):
  987.         self.pairs = []
  988.  
  989.     
  990.     def __cmp__(self, other):
  991.         return cmp(self.pairs, other.pairs)
  992.  
  993.     
  994.     def __hash__(self):
  995.         return hash(self.pairs)
  996.  
  997.     
  998.     def __repr__(self):
  999.         return 'IntSet(%r, %r, %r)' % (self.tostring(), self.sep, self.rng)
  1000.  
  1001.     
  1002.     def normalize(self):
  1003.         self.pairs.sort()
  1004.         i = 1
  1005.         while i < len(self.pairs):
  1006.             (alo, ahi) = self.pairs[i - 1]
  1007.             (blo, bhi) = self.pairs[i]
  1008.             if ahi >= blo - 1:
  1009.                 self.pairs[i - 1:i + 1] = [
  1010.                     (alo, max(ahi, bhi))]
  1011.                 continue
  1012.             i = i + 1
  1013.  
  1014.     
  1015.     def tostring(self):
  1016.         s = ''
  1017.         for lo, hi in self.pairs:
  1018.             if lo == hi:
  1019.                 t = repr(lo)
  1020.             else:
  1021.                 t = repr(lo) + self.rng + repr(hi)
  1022.             if s:
  1023.                 s = s + self.sep + t
  1024.                 continue
  1025.             s = t
  1026.         
  1027.         return s
  1028.  
  1029.     
  1030.     def tolist(self):
  1031.         l = []
  1032.         for lo, hi in self.pairs:
  1033.             m = range(lo, hi + 1)
  1034.             l = l + m
  1035.         
  1036.         return l
  1037.  
  1038.     
  1039.     def fromlist(self, list):
  1040.         for i in list:
  1041.             self.append(i)
  1042.         
  1043.  
  1044.     
  1045.     def clone(self):
  1046.         new = IntSet()
  1047.         new.pairs = self.pairs[:]
  1048.         return new
  1049.  
  1050.     
  1051.     def min(self):
  1052.         return self.pairs[0][0]
  1053.  
  1054.     
  1055.     def max(self):
  1056.         return self.pairs[-1][-1]
  1057.  
  1058.     
  1059.     def contains(self, x):
  1060.         for lo, hi in self.pairs:
  1061.             if x <= x:
  1062.                 pass
  1063.             elif x <= hi:
  1064.                 return True
  1065.                 continue
  1066.         
  1067.         return False
  1068.  
  1069.     
  1070.     def append(self, x):
  1071.         for i in range(len(self.pairs)):
  1072.             (lo, hi) = self.pairs[i]
  1073.             if x < lo:
  1074.                 if x + 1 == lo:
  1075.                     self.pairs[i] = (x, hi)
  1076.                 else:
  1077.                     self.pairs.insert(i, (x, x))
  1078.                 if i > 0 and x - 1 == self.pairs[i - 1][1]:
  1079.                     self.pairs[i - 1:i + 1] = [
  1080.                         (self.pairs[i - 1][0], self.pairs[i][1])]
  1081.                 
  1082.                 return None
  1083.             
  1084.             if x <= hi:
  1085.                 return None
  1086.                 continue
  1087.         
  1088.         i = len(self.pairs) - 1
  1089.         if i >= 0:
  1090.             (lo, hi) = self.pairs[i]
  1091.             if x - 1 == hi:
  1092.                 self.pairs[i] = (lo, x)
  1093.                 return None
  1094.             
  1095.         
  1096.         self.pairs.append((x, x))
  1097.  
  1098.     
  1099.     def addpair(self, xlo, xhi):
  1100.         if xlo > xhi:
  1101.             return None
  1102.         
  1103.         self.pairs.append((xlo, xhi))
  1104.         self.normalize()
  1105.  
  1106.     
  1107.     def fromstring(self, data):
  1108.         new = []
  1109.         for part in data.split(self.sep):
  1110.             list = []
  1111.             for subp in part.split(self.rng):
  1112.                 s = subp.strip()
  1113.                 list.append(int(s))
  1114.             
  1115.             if len(list) == 1:
  1116.                 new.append((list[0], list[0]))
  1117.                 continue
  1118.             if len(list) == 2 and list[0] <= list[1]:
  1119.                 new.append((list[0], list[1]))
  1120.                 continue
  1121.             raise ValueError, 'bad data passed to IntSet'
  1122.         
  1123.         self.pairs = self.pairs + new
  1124.         self.normalize()
  1125.  
  1126.  
  1127.  
  1128. def pickline(file, key, casefold = 1):
  1129.     
  1130.     try:
  1131.         f = open(file, 'r')
  1132.     except IOError:
  1133.         return None
  1134.  
  1135.     pat = re.escape(key) + ':'
  1136.     if casefold:
  1137.         pass
  1138.     prog = re.compile(pat, re.IGNORECASE)
  1139.     while None:
  1140.         line = f.readline()
  1141.         if not line:
  1142.             break
  1143.         
  1144.         if prog.match(line):
  1145.             text = line[len(key) + 1:]
  1146.             while None:
  1147.                 line = f.readline()
  1148.                 if not line or not line[0].isspace():
  1149.                     break
  1150.                 
  1151.                 text = text + line
  1152.             return text.strip()
  1153.             continue
  1154.  
  1155.  
  1156. def updateline(file, key, value, casefold = 1):
  1157.     
  1158.     try:
  1159.         f = open(file, 'r')
  1160.         lines = f.readlines()
  1161.         f.close()
  1162.     except IOError:
  1163.         lines = []
  1164.  
  1165.     pat = re.escape(key) + ':(.*)\n'
  1166.     if casefold:
  1167.         pass
  1168.     prog = re.compile(pat, re.IGNORECASE)
  1169.     if value is None:
  1170.         newline = None
  1171.     else:
  1172.         newline = '%s: %s\n' % (key, value)
  1173.     for i in range(len(lines)):
  1174.         line = lines[i]
  1175.         if prog.match(line):
  1176.             if newline is None:
  1177.                 del lines[i]
  1178.             else:
  1179.                 lines[i] = newline
  1180.             break
  1181.             continue
  1182.     elif newline is not None:
  1183.         lines.append(newline)
  1184.     
  1185.     tempfile = file + '~'
  1186.     f = open(tempfile, 'w')
  1187.     for line in lines:
  1188.         f.write(line)
  1189.     
  1190.     f.close()
  1191.     os.rename(tempfile, file)
  1192.  
  1193.  
  1194. def test():
  1195.     global mh, f
  1196.     os.system('rm -rf $HOME/Mail/@test')
  1197.     mh = MH()
  1198.     
  1199.     def do(s):
  1200.         print s
  1201.         print eval(s)
  1202.  
  1203.     do('mh.listfolders()')
  1204.     do('mh.listallfolders()')
  1205.     testfolders = [
  1206.         '@test',
  1207.         '@test/test1',
  1208.         '@test/test2',
  1209.         '@test/test1/test11',
  1210.         '@test/test1/test12',
  1211.         '@test/test1/test11/test111']
  1212.     for t in testfolders:
  1213.         do('mh.makefolder(%r)' % (t,))
  1214.     
  1215.     do("mh.listsubfolders('@test')")
  1216.     do("mh.listallsubfolders('@test')")
  1217.     f = mh.openfolder('@test')
  1218.     do('f.listsubfolders()')
  1219.     do('f.listallsubfolders()')
  1220.     do('f.getsequences()')
  1221.     seqs = f.getsequences()
  1222.     seqs['foo'] = IntSet('1-10 12-20', ' ').tolist()
  1223.     print seqs
  1224.     f.putsequences(seqs)
  1225.     do('f.getsequences()')
  1226.     for t in reversed(testfolders):
  1227.         do('mh.deletefolder(%r)' % (t,))
  1228.     
  1229.     do('mh.getcontext()')
  1230.     context = mh.getcontext()
  1231.     f = mh.openfolder(context)
  1232.     do('f.getcurrent()')
  1233.     for seq in [
  1234.         'first',
  1235.         'last',
  1236.         'cur',
  1237.         '.',
  1238.         'prev',
  1239.         'next',
  1240.         'first:3',
  1241.         'last:3',
  1242.         'cur:3',
  1243.         'cur:-3',
  1244.         'prev:3',
  1245.         'next:3',
  1246.         '1:3',
  1247.         '1:-3',
  1248.         '100:3',
  1249.         '100:-3',
  1250.         '10000:3',
  1251.         '10000:-3',
  1252.         'all']:
  1253.         
  1254.         try:
  1255.             do('f.parsesequence(%r)' % (seq,))
  1256.         except Error:
  1257.             msg = None
  1258.             print 'Error:', msg
  1259.  
  1260.         stuff = os.popen('pick %r 2>/dev/null' % (seq,)).read()
  1261.         list = map(int, stuff.split())
  1262.         print list, '<-- pick'
  1263.     
  1264.     do('f.listmessages()')
  1265.  
  1266. if __name__ == '__main__':
  1267.     test()
  1268.  
  1269.